import React, { useEffect, useMemo, useRef } from 'react';
import PropTypes from 'prop-types';
import { Form, Table } from 'antd';
import { useBoolean } from 'ahooks';

import styles from './index.less';

const EditableContext = React.createContext();

const EditableRow = ({ form, index, ...props }) => (
    <EditableContext.Provider value={form}>
        <tr {...props} />
    </EditableContext.Provider>
);

const EditableFormRow = Form.create()(EditableRow);

const EditableCell = ({ editable, column, record, index, handleSave, children, ...restProps }) => {
    const [editing, { toggle: toggleEditing }] = useBoolean(false);
    const formRef = useRef(null);
    const formItemRef = useRef(null);

    useEffect(() => {
        if (editing && formItemRef.current) {
            formItemRef.current.focus();
        }
    }, [editing]);

    const save = e => {
        formRef.current.validateFields((error, values) => {
            // 异常需要通过 handleSave 抛出
            if (error && error[e.currentTarget.id] && !column.throwError) {
                return;
            }
            toggleEditing();
            if (typeof handleSave === 'function') {
                handleSave({ ...record, ...values }, error);
            }
        });
    };

    const renderCell = form => {
        formRef.current = form;
        const { dataIndex, formItem, formItemOptions = {} } = column;
        let formItemContent = formItem;
        if (typeof formItem === 'function') {
            formItemContent = formItem(record);
        }
        const isFormItemValid = React.isValidElement(formItemContent);
        const formItemWithRef = isFormItemValid
            ? React.cloneElement(formItemContent, {
                  ref: formItemRef,
                  onPressEnter: save,
                  onBlur: save,
              })
            : null;
        return editing && isFormItemValid ? (
            <Form.Item style={{ margin: 0 }}>
                {form.getFieldDecorator(dataIndex, {
                    initialValue: record[dataIndex],
                    ...formItemOptions,
                })(formItemWithRef)}
            </Form.Item>
        ) : (
            <div className={styles.editableCellWrap} onClick={toggleEditing}>
                {children}
            </div>
        );
    };

    return (
        <td {...restProps}>
            {editable ? <EditableContext.Consumer>{renderCell}</EditableContext.Consumer> : children}
        </td>
    );
};

const EditableTable = ({ className, columns = [], handleCellChange, ...restProps }) => {
    const advancedColumns = useMemo(() => {
        return columns.map(col => {
            const { editable } = col;
            if (!col.editable) {
                return col;
            }
            return {
                ...col,
                onCell: record => ({
                    column: col,
                    record,
                    editable: typeof editable === 'function' ? editable(record) : editable,
                    handleSave: handleCellChange,
                }),
            };
        });
    }, [columns, handleCellChange]);

    const components = {
        body: {
            row: EditableFormRow,
            cell: EditableCell,
        },
    };

    return (
        <Table
            {...restProps}
            className={`${styles.editableTable} ${className}`}
            components={components}
            rowClassName={() => styles.editableRow}
            columns={advancedColumns}
        />
    );
};

EditableTable.propTypes = {
    columns: PropTypes.array,
    dataSource: PropTypes.array,
    handleCellChange: PropTypes.func,
};

export default EditableTable;
